﻿using System.Security.Cryptography;
using Viyi.Util.Codec;

namespace Signit.Sdk.Util
{
    internal static class InternalTool
    {
        #region 对称加密算法模板
        public static byte[] Encrypt(SymmetricAlgorithm algorithm, byte[] data, byte[] iv, byte[] key)
        {
            if (data == null) { return null; }

            key = CheckAndPadKey(key, algorithm);
            ICryptoTransform transform = algorithm.CreateEncryptor(key, iv);
            return transform.TransformFinalBlock(data, 0, data.Length);
        }

        public static byte[] Encrypt(SymmetricAlgorithm algorithm, byte[] data, out byte[] iv, byte[] key)
        {
            if (data == null)
            {
                iv = null;
                return null;
            }

            key = CheckAndPadKey(key, algorithm);
            iv = algorithm.IV;
            ICryptoTransform transform = algorithm.CreateEncryptor(key, iv);
            return transform.TransformFinalBlock(data, 0, data.Length);
        }

        internal static byte[] Encrypt(
            SymmetricAlgorithm algorithm,
            byte[] data, int start, int length,
            byte[] iv, byte[] key)
        {
            if (data == null) { return null; }
            key = CheckAndPadKey(key, algorithm);
            ICryptoTransform transform = algorithm.CreateEncryptor(key, iv);
            return transform.TransformFinalBlock(data, start, length);
        }

        public static byte[] Decrypt(SymmetricAlgorithm algorithm, byte[] data, byte[] iv, byte[] key)
        {
            if (data == null) { return null; }

            key = CheckAndPadKey(key, algorithm);
            ICryptoTransform transform = algorithm.CreateDecryptor(key, iv);
            return transform.TransformFinalBlock(data, 0, data.Length);
        }
        #endregion

        #region 根据加密算法要求补齐Key

        private static readonly byte[] PadingSalt
            = "4KC8A0HCSLFDCPDREVQUVYYCPHB9L549UQ0WITFITWV19D8751QEXN6FTPIT8EGW".HexDecode();

        private const int PaddingIterations = 64;

        /// <summary>
        /// 使用PBKDF2算法补齐密码
        /// </summary>
        /// <param name="key"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        internal static byte[] PadKey(byte[] key, int length)
        {
            return key == null
                ? null
                : new Rfc2898DeriveBytes(key, PadingSalt, PaddingIterations).GetBytes(length);
        }

        /// <summary>
        /// 检查key的长度对算法来说是否合法，如果合法，直接返回。
        /// 如果不合法，使用PBKDF2算法对其进行补齐至最接近的长度，然后返回
        /// </summary>
        /// <param name="key"></param>
        /// <param name="algorithm"></param>
        /// <returns></returns>
        private static byte[] CheckAndPadKey(byte[] key, SymmetricAlgorithm algorithm)
        {
            if (key == null) { return null; }

            int length = key.Length * 8;
            foreach (KeySizes sizes in algorithm.LegalKeySizes)
            {
                // 不在范围内，key无效
                if (length < sizes.MinSize || length > sizes.MaxSize)
                {
                    continue;
                }

                // 在范围内，又没有步长限制，key有效
                if (sizes.SkipSize == 0) { return key; }

                // 检查如果key的长度是合法长度，不需要Padding，直接返回
                if ((length - sizes.MinSize) % sizes.SkipSize == 0)
                {
                    return key;
                }
            }

            // 检查key的长度不属于任何合法长度
            KeySizes firstSizes = algorithm.LegalKeySizes[0];
            int paddingLength;
            if (length < firstSizes.MinSize)
            {
                paddingLength = firstSizes.MinSize;
            }
            else if (length > firstSizes.MaxSize)
            {
                paddingLength = firstSizes.MaxSize;
            }
            else
            {
                paddingLength = length + (length - firstSizes.MinSize) % firstSizes.SkipSize;
            }

            return PadKey(key, paddingLength / 8);
        }
        #endregion
    }
}
